#pragma once
#include "Allocator.h"
#include "Iterator.h"
#include "Trait.h"
#include "Algorithm.h"

namespace mini
{
	template<typename T, typename Allocate = TAllocator<T> >
	class TVector
	{
	public:
		typedef T value_type;
		typedef value_type* pointer;
		typedef value_type& reference;
		typedef const value_type* const_pointer;
		typedef value_type* iterator;
		typedef const value_type* const_iterator;
		typedef size_t size_type;
	
	public:
		TVector()
		{
		}
		
		TVector(size_type n)
		{
			allocateAndFill(n, T());
		}

		TVector(size_type n, const T& value)
		{
			allocateAndFill(n, value);
		}
		~TVector()
		{
			Allocate::destory(mFirst);
			deallocate();
		}
		iterator begin()
		{
			return mFirst;
		}
		iterator end()
		{
			return mEnd;
		}
		size_type capacity()
		{
			return mStoreCount;
		}
		
		reference front()
		{
			return *begin();
		}

		reference back()
		{
			return *(begin() + mElement);
		}

		bool empty()const
		{
			return mFirst == mEnd ? true : false;
		}

		reference operator[](size_type n)
		{
			return *(begin() + n);
		}
		void clear()
		{
			if (!empty())
			{
				for (iterator item = mFirst; item != mEnd; ++item)
				{
					Allocate::destory(item);
				}
				mElement = 0;
				mEnd = mFirst;
			}
		}

		void pushBack(const T& value)
		{
			if (mLast == mEnd)
			{
				T* pNewAddress = nullptr;
				size_type newSize = 4;
				if (nullptr != mLast)
				{
					size_t oldSize = mLast - mFirst;
					newSize = oldSize + oldSize / 2;
					pNewAddress = static_cast<T*>(Allocate::allocate(newSize));
					memmove(pNewAddress, mFirst, oldSize * sizeof(T) );
					mFirst = pNewAddress;
					mEnd = mFirst + oldSize;
					mElement = oldSize;
				}
				else
				{
					mFirst = static_cast<T*>(Allocate::allocate(newSize));
					mEnd = mFirst;
				}
				mLast = mFirst + newSize;
				mStoreCount = newSize;
			}
			Allocate::construct(mEnd, value);
			++mEnd;
			++mElement;
		}

		void popBack()
		{
			if (!empty())
			{
				Allocate::destory(--mEnd);
				--mElement;
			}
		}

		size_type size()
		{
			return mElement;
		}
	private:
		iterator allocateAndFill(size_type n, const T& value)
		{
			mFirst = static_cast<T*>(Allocate::allocate(n));
			mEnd = uninitializedFillN(mFirst, n, value) + n;
			mLast = mEnd;
			mStoreCount = n;
			mElement = n;
			return begin();
		}
		void deallocate()
		{
			if (nullptr != mFirst)
			{
				Allocate::deallocate(mFirst, mStoreCount * sizeof(T));
			}
		}
	private:
		iterator	mFirst = nullptr;		// pointer to beginning of array
		iterator	mLast = nullptr;		// pointer to current end of sequence
		iterator	mEnd = nullptr;			// pointer to end of array
		size_type	mElement = 0;
		size_type   mStoreCount = 0;
	};
}